/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pl.abstractvoid.gui;

import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.text.DefaultCaret;
import pl.abstractvoid.distributions.AbstractDistribution;
import pl.abstractvoid.languages.LanguageProvider;

/**
 * Component responsible for emulating R console. Two JTextArea pairs are used:
 * one for input commands, one for output. These tho components are encapsuleted
 * within RTextAreaPair class.
 * @author Wojciech Szałapski
 * @author Ivo Zieliński
 */
public class RConsolePanel extends javax.swing.JPanel implements DistributionStateObserver {

    private HashMap<AbstractDistribution, RTextAreaPair> consoles = new HashMap();
    private RTextAreaPair currentConsole;
    private final JTextArea nullDistributionInputArea = new JTextArea();
    private final JTextArea nullDistributionOutputArea = new JTextArea();
    private final JScrollPane nullDistributionInputPane = new JScrollPane(nullDistributionInputArea);
    private final JScrollPane nullDistributionOutputPane = new JScrollPane(nullDistributionOutputArea);
    /**
     * Creates new form RConsolePanel
     */
    private final MainFrame mediator;
    private Font font;

    /**
     * Creates new form DanePanel
     *
     * @param mediator
     */
    public RConsolePanel(MainFrame mediator) {
        this.mediator = mediator;
        loadFont();
        initComponents();
        nullDistributionInputArea.setEnabled(false);
        nullDistributionOutputArea.setEditable(false);
        if (font != null) {
            nullDistributionOutputArea.setFont(font);
        }
    }

    /**
     * Display RTextArea pair associated with currently selected dsitribution.
     */
    @Override
    public void distributionChangedNotify() {
        currentConsole = consoles.get(mediator.getCurrentDistribution());
        updateComponents();
    }

    /**
     * Created a new RTextArea pair for the new distribution and display it.
     */
    @Override
    public void newDistributionNotify() {
        consoles.put(mediator.getCurrentDistribution(), new RTextAreaPair(mediator));
        currentConsole = consoles.get(mediator.getCurrentDistribution());
        updateComponents();
    }

    /**
     * Remove RTetArea pair associated with the deleted distribution.
     * @param lastInstanceDeleted 
     */
    @Override
    public void distributionDeletedNotify(boolean lastInstanceDeleted) {
        consoles.remove(mediator.getCurrentDistribution());
        if (lastInstanceDeleted) {
            loadInitialState();
        }
    }

    /**
     * Set view on RTextArea components associated with currently selected
     * distribution
     */
    private void updateComponents() {
        consoleSplitPane.setLeftComponent(new JScrollPane(currentConsole.rOutputTextArea));
        consoleSplitPane.setRightComponent(new JScrollPane(currentConsole.rInputTextArea));
        //Reditect System.out to JTextArea of current distribution
        System.setOut(currentConsole.printStream);
        mediator.getCurrentDistribution().getrState().redirectOutput();
        updateUI();
    }

    /**
     * Returns R commands entered in JTextArea responsible for input.
     * @return 
     */
    public String getInputCode() {
        String code = currentConsole.rInputTextArea.getText();
        currentConsole.rInputTextArea.setText(null);
        return code;
    }

    /**
     * Sets the default initial state when no distributions exist yet.
     */
    public void loadInitialState() {
        nullDistributionOutputArea.setText(LanguageProvider.getInstance().getString("rConsoleInitialMessage"));
        consoleSplitPane.setLeftComponent(nullDistributionOutputPane);
        consoleSplitPane.setRightComponent(nullDistributionInputPane);
        updateUI();
    }
    
    /**
     * Displays the initial message in each output JTextArea in the appropriate
     * language. Invoked when the language is changed.
     */
    public void reloadInitialMessage() {
        for (Map.Entry p : consoles.entrySet()) {
            RTextAreaPair textAreaPair = (RTextAreaPair) p.getValue();
            textAreaPair.rOutputTextArea.append("\n" + LanguageProvider.getInstance().getString("languageChanged"));
            textAreaPair.rOutputTextArea.append("\n" + LanguageProvider.getInstance().getString("rPromptConsoleReady") + "\n");
        }
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        consoleSplitPane = new javax.swing.JSplitPane();
        outputAreaScrollPane = new javax.swing.JScrollPane();
        outputTextArea = new javax.swing.JTextArea();
        inputAreaScrollPane = new javax.swing.JScrollPane();
        inputTextArea = new javax.swing.JTextArea();

        consoleSplitPane.setBorder(null);
        consoleSplitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
        consoleSplitPane.setResizeWeight(0.8);

        outputTextArea.setColumns(20);
        outputTextArea.setRows(5);
        if (font != null) {
            outputTextArea.setFont(font);
        }
        outputTextArea.setText(LanguageProvider.getInstance().getString("rConsoleInitialMessage"));
        outputAreaScrollPane.setViewportView(outputTextArea);
        outputTextArea.setEditable(false);

        consoleSplitPane.setTopComponent(outputAreaScrollPane);

        inputTextArea.setColumns(20);
        inputTextArea.setRows(5);
        inputTextArea.setEnabled(false);
        inputAreaScrollPane.setViewportView(inputTextArea);

        consoleSplitPane.setRightComponent(inputAreaScrollPane);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(consoleSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(consoleSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)
        );
    }// </editor-fold>//GEN-END:initComponents
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JSplitPane consoleSplitPane;
    private javax.swing.JScrollPane inputAreaScrollPane;
    private javax.swing.JTextArea inputTextArea;
    private javax.swing.JScrollPane outputAreaScrollPane;
    private javax.swing.JTextArea outputTextArea;
    // End of variables declaration//GEN-END:variables

    /**
     * Loads the font for JTextAreas.
     */
    private void loadFont() {
        InputStream istream = getClass().getResourceAsStream("/Fonts/LiberationMono-Regular.ttf");
        Font myFont;
        try {
            myFont = Font.createFont(Font.TRUETYPE_FONT, istream);
            font = myFont.deriveFont(12.0f);
        } catch (FontFormatException | IOException ex) {
            Logger.getLogger(RConsolePanel.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @Override
    public void distributionsLoadedNotify() {
        consoles = new HashMap<>();
    }

    void setOutputAreaText(String text) {
        outputTextArea.setText(text);
    }

    /**
     * Class encapsulating two JTextAreas, one for R input and one for R input.
     * Together they emulate the R console.
     */
    class RTextAreaPair {

        public JTextArea rOutputTextArea, rInputTextArea;
        public PrintStream printStream;
        public final MainFrame mediator;

        public RTextAreaPair(MainFrame mediator) {
            this.mediator = mediator;
            this.rInputTextArea = new JTextArea();
            rInputTextArea.setLineWrap(true);
            if (font != null) {
                rInputTextArea.setFont(font);
            }
            //Allow for code execution with alt+r
            rInputTextArea.addKeyListener(new KeyAdapter() {
                @Override
                public void keyPressed(KeyEvent e) {
                    super.keyPressed(e);
                    if (e.isAltDown() && e.getKeyChar() == 'r') {
                        notifyCodeExecuted();
                    } else if (e.isControlDown() && e.getKeyCode() == KeyEvent.VK_L) {
                        rOutputTextArea.setText("");
                    }
                }
            });
            this.rOutputTextArea = new JTextArea();
            rOutputTextArea.setLineWrap(true);
            rOutputTextArea.setEditable(false);
            if (font != null) {
                rOutputTextArea.setFont(font);
            }
            ((DefaultCaret) rOutputTextArea.getCaret()).setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
            printStream = new PrintStream(new ConsoleAreaOutputStream(rOutputTextArea));
        }

        private void notifyCodeExecuted() {
            mediator.notifyCodeExecuted();
        }
    }
}
